home *** CD-ROM | disk | FTP | other *** search
- #!/bin/csh
-
- set version=1.5
-
- # Copyright (C) Kenneth L. Manheimer 1988, 1989
- # National Institute of Standards and Technology, Gaithersberg, MD 20899
- # klm@cme.nist.gov or ..!uunet!cme-durer!klm (301) 975-3539
-
- # This script is distributed in the hope that it will be useful, but
- # WITHOUT ANY WARRANTY. Neither the National Institute of Standards
- # and Technology, nor any author or distributor, accepts responsibility
- # to anyone for the consequences of using it or for whether it serves
- # any particular purpose or works at all, unless he says so in writing.
-
- # Call by:
- #
- # 8mmbackup [ verify ]
- # or
- # fullPrimer [ inhibit ]
- #
- # Where 'verify' means just do preliminary variable assignments and fundamental
- # configuration verification (to validate presence of script, accounting,
- # subject directories, etc). On first-ever dump (ie, no 'seqIDfile' present)
- # both the contents and the registry are exhaustive, regardless of whether full
- # or incremental backup is indicated or 'exhaustiveRegistry' is configured.
- # Otherwise (and generally): incrementals are run unless 'fullPrimer'
- # has been invoked (in default mode) since last backup, in which case
- # a full backup will be performed. See man page for more complete
- # instructions.
-
-
- # This script performs online backup of directory structures, implementing:
- # - both exhaustive ("full") and incremental ("incr") culling of directory
- # hierarchies, or just preparatory verification of configuration ("verify")
- # - registry of those saved files that were changed since previous backup
- # - exclusion of specified directories (eg, tmp, spool, lost+found,...),
- # - exclusion of specified paths (eg, remote mounts, standard distribution,...)
- # NOTE that the method employed is oriented for very high capacity backup
- # medium (at our site, an Exabyte 8mm video-tape drive); in particular, it is
- # necessary that the medium is able to accomodate at least an entire full
- # backup fit on a single volume.
-
- # Change History - moved to seperate file 'Changelog'
-
-
- #vvvvvvvvvvvvvvvvv User designated (configuration) variables vvvvvvvvvvvvvvvvvv
- # The best way to configure this script is to
- # (1) Adjust the setting of 'scriptDir' below to the correct directory, and
- # (2) Put a copy of this section ("User designated ...") of the script
- # in the 'outboardConfig' file and use that copy to make all your
- # configuration settings instead of this one. Since the 8mm.config file is
- # not included as part of the shar distribution, this has the advantage
- # that you won't need recreate your configuration every time you implement
- # a new release. The disadvantage is that you will have to make sure to
- # track the more infrequent configuration section changes when they occur.
-
- # Set 'scriptDir' to the directory where the backup scripts (including
- # this one) reside:
- set scriptDir=/usr/local/lib/8mmbackup
-
- # Set 'outboardConfig' to the name of the config file you want to use so you
- # don't have to reassert your configuration every time you get a new version of
- # the script...
- set outboardConfig=$scriptDir/8mm.config
-
- # Setting debug causes the output device to be /dev/null (and tape
- # control processing to be inhibited) and the target directories to be
- # only the 8mmbackup directory (and subdirs). Also, all logging goes to
- # terminal only (rather than both terminal and log file). Giving debug the
- # value 'verbose' causes very verbose operation.
- #set debug=verbose
- #set debug
-
- # Set 'subjPaths' to a list of directory hierarchies that backup should cover.
- # Wild carding can be used in 'subjPaths' and 'excludePaths'.
- # eg 'set subjPaths=(/)' or 'set subjPaths=(/lib /etc /util/news)', ...
- set subjPaths=(/ /usr/spool/{mail,mqueue})
-
- # Set 'excludePaths' to specific rooted paths of directories whose contents are
- # to be excluded from backup; use this, for instance, to exclude specific file
- # systems that are preserved elsewhere (eg, nfs mounts) or parts of the
- # standard distribution you'd prefer to rebuild rather than restore... It's
- # unnecessary to duplicate entries excluded by 'excludeDirs' (below).
- # NOTE that it will *not* work to try to try to include something in subjPaths
- # that is contained within a directory structure excluded in excludePaths
- # - *all* offspring in excludePaths hierarchies are excluded.
- # eg: 'set excludePaths=(/usr/man/cat* /home/{norman,squire} /depot)
- set excludePaths=(/usr/man/cat*)
-
- # Set 'excludeDirs' to pathless directory names whose contents are to be
- # excluded from preservation. Csh wild card chars will work (see 'find' man
- # page), but be certain to escape them for the shell.
- # We use: 'set excludeDirs=(swap tmp spool)'
- set excludeDirs=(swap tmp newsgroups spool)
-
- # BEING RETIRED
- ## Set 'exhaustiveRegistry' if you want the registry to contain the
- ## names of *all* the files that have been preserved, not just those
- ## that were modified since the previous backup. (Otherwise the names
- ## of those files that have not changed since the prior backup will not
- ## be registered, though they are, of course, still preserved during a
- ## full backup.)
- #set exhaustiveRegistry
-
- # Set 'realDev' to the no-rewind device that should receive the backup.
- # Tape control will always be applied to 'realDev', but when debug is
- # set the archives will be sent to /dev/null.
- set realDev=/dev/nrst1
-
- # Set totalCap to (conservative) total capacity (in bytes) a single volume
- # holds. We use 2.2 billion (2200000000) for the exabyte drive.
- set totalCap=2200000000
-
- # Set 'errorNotificeTo' to a username where a mail message should be sent
- # in case of backup glitch. (We use an alias, 'backupmaster', which
- # translates to the concerned parties.)
- set errorNoticeTo=backupmaster
-
- # Set 'verifyTolerance' if you want a fairly stringent but time consuming
- # review of the archive file to be done after the archive is written to tape.
- # Assign an integer value that will be taken as a +/- tolerance between the
- # roster of files designated for archival and a Table-Of-Contents of the
- # archive written to tape. The tolerance is necessary for discrepancies
- # between the roster for archival and the archive contents due to things
- # like files that were deleted after file-system-scan but before archive
- # creation. The tolerance will expose insidious archive corruption like disk
- # read failures, which cause cpio to corrupt the archive without indication
- # during writing. The default is a 2% tolerance (ie, # of entries within 2% of
- # basis); use a larger number only if you need to do a backup during
- # substantial file-system modifications. Make 'verifyTolerance' unset
- # if you want to have a much less assured but much faster verification
- # of the backup archive.
- set verifyTolerance=2
- #^^^^^^^^^^^^^^^^^^ User designated (configuration) variables ^^^^^^^^^^^^^^^^^
-
-
-
- #vvvvvvvvvvvvvvvvvvvvvvvvvvv Get outboard config vvvvvvvvvvvvvvvvvvvvvvvvvvv
- if (-e $outboardConfig) then
- source $outboardConfig
- endif
- #^^^^^^^^^^^^^^^^^^^^^^^^^^^ Got outboard config ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-
-
- #vvvvvvvvvvvvvvvvvvvvvvvvvvvv Contrived variables vvvvvvvvvvvvvvvvvvvvvvvvvvvvv
- # (... and logging and error aliases)
-
- # Script name -
- if ($?0) then
- set scNm=$0; set scNm=$scNm:t
- else
- set scNm=BackupScript
- endif
-
- # dateStamp - date of backup, reset just before interlock claim below:
- set dateStamp="`date`"
-
- # guard alias - set onintr label and guardState for guardedExit (below)
- alias guard "set guardState='\!*'; onintr \!*"
- guard ground
-
- # guardedExit alias - exit with value through current guard
- alias guardedExit "set exitVal=\!*; goto" '$guardState'
- set exitVal=1
-
- # interactive - 1 if running from a terminal, 0 elsewise
- set interactive=1; tty -s; if ($status == 1) set interactive=0
-
- # acctDir - directory where accounting for backup processing is maintained:
- set acctDir=$scriptDir/accounting
-
- # interlock - file present when backup already in progress.
- set interlock=$acctDir/interlock
-
- # fullTrigger - file whose presence triggers full backup
- set fullTrigger=$acctDir/fullTrigger
-
- # log - file where conduct and errors of backup session is logged
- set logfile=$acctDir/backup.log
-
- # toLog alias - command used for logging messages:
- alias tolog "(\!*) |& tee -a $logfile"
-
- # incrClause - find parameter which indicates file, with mod-time of last
- # backup, to respect for incremental preserves:
- set incrClause=""
-
- # pruneClause - 'excludeDirs' massaged (below) for use in find statement:
- set pruneClause=""
-
- # awkFilterScript - file where (file-name/capacity filter) awk script resides:
- set awkFilterScript=$scriptDir/fnFilter.awk
-
- # fgrepClause - 'excludePaths' massaged (below) for expression as fgrep filter;
- # wild card globbing and symbolic links are expanded to
- # their unravelled equivalents.
- set fgrepClause=""
-
- # fgrepClauseFile - fgrep clause residence (instead of passing thru cmd line)
- set fgrepClauseFile=$acctDir/fgrep.clause
-
- # excludePathFilter - like the name says...
- alias excludePathsFilter "fgrep -vf $fgrepClauseFile"
-
- # seqID - Sequence number identifying current backup *and* tape index.
- # Each full backup re-initiates to seqID 0, so new tape must be used.:
- set seqID=""
-
- # seqIDFile - File containing identifying sequence number of previous backup.
- # '0' for full backup.
- set seqIDFile=$acctDir/seqIDFile
-
- # newSeqIDFile - The seqID for backup currently in progress:
- set newSeqIDFile=$seqIDFile.current
-
- # seqStatsFile - table translating sequence registry IDs to actual dates:
- # format: seqID tape-capacity-consumed toDev mode (subjPaths) (excludePaths) \
- # (excludeDirs) date
- set seqStatsFile=$acctDir/seqStats
-
- # priorStatsFile - seqStats of prior cycle, exists only during full backup, for
- # similar purpose as that of priorRegistry (below)
- set priorStatsFile=$acctDir/seqStats.prior
-
- # capUsed - total amount of tape used so far (obtained from 'seqStats'):
- set capUsed=""
-
- # capPending - amt bytes pending to go onto tape this dump:
- set capPending=""
-
- # pends - working file where roster of files that have changed since previous
- # backup is registered. (This will always consist of only those files
- # that have changed since last backup, even when additional files (ie,
- # during full backup) are being preserved.):
- # format: '-ls' format of find
- set pends=$acctDir/pends
-
- # fullPends - working roster of files pending full dump (not just changed ones)
- set fullPends=$acctDir/fullPends
-
- # registry - Current cycle's cumulative file of filenames whose contents were
- # preserved *due to having been modified since prior backup*. Ie,
- # even during full backup, only recently-modified files are
- # registered, though any files under 'subjectsPath' are saved then.
- #
- # Format of registry entry: <file-path> <seq-id> [<seq-id> ...]
- #
- # where <seq-id> is is the 'seqID' of each previous backup (up to
- # and including the most recent full backup) that preserved a
- # version of <file-path>.
- #
- # This file is saved with subsequent cycle's full backup. Thus each
- # cycle's registry is available at beginning of subsequent cycle's
- # tape.
- set registry=$acctDir/registry
-
- #priorRegistry - exists only during full backup, so prior cycles registry will
- # be saved with full backup. Emplaced just prior to computing
- # pends and removed after archival.
- set priorRegistry=$acctDir/registry.prior
-
- # 'toDev' is where the archives are actually directed. Normally it's
- # the same as $realDev, but during debug its just the /dev/null sink.
- if ($?debug == 1) then
- if ("$debug" == "verbose") set echo
- set toDev=/dev/null
- set subjPaths=($scriptDir/accounting)
- alias tolog '(\!*) |& cat'
- else
- set toDev=$realDev
- endif
-
- #8mmin/8mmout - filter interfaces to be used for access to 8mm tape device.
- alias 8mmin dd if='\!\!:1' ibs=5120
- alias 8mmout dd of='\!\!:1' obs=5120
- #^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Contrived variables ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-
-
-
- #vvvvvvvvvvvvvvvv Configuration verification and reformulation vvvvvvvvvvvvvvvv
- # For bozos that create silly aliases in their root accounts (:-)
- unalias rm
- unalias mv
- unalias cd
-
- # Verify existence of 'scriptDir' and establish 'acctDir':
-
- if (! -d $scriptDir) then
- echo CONFIG ERROR: designated scriptDir \"$scriptDir\" does not exist
- guardedExit 1 # ====>
- else if (! -e $acctDir) then
- mkdir $acctDir
- touch $fullTrigger
- endif
-
- # Establish 'logfile' if necessary:
- if (! -e $logfile) touch $logfile
-
-
-
- #vvvvvvvvvvvvvvvvvvvvvvvvv backup vs priming branch vvvvvvvvvvvvvvvvvvvvvvvvvvv
- if ("$scNm" == "fullPrimer") then
- if ("$1" == "") then
- if (! -e $fullTrigger) then
- touch $fullTrigger
- tolog echo "${scNm}: Primed system for full backup `date`"
- else
- echo "${scNm}: System already primed for full backup `date`"
- endif
- echo \'${0} inhibit\' to cancel
- else if ("$1" == "inhibit") then
- if (-e $fullTrigger) then
- rm -f $fullTrigger
- tolog echo "${scNm}: Primed system for incremental backup `date`"
- else
- echo "${scNm}: System already primed for incremental backup `date`"
- endif
- echo \'${0}\' to enable full backup
- else
- echo "Usage: ${scNm} [ inhibit ]"
- endif
- guardedExit 0 # ====>
- endif
- #^^^^^^^^^^^^^^^^^^^^^^^^^ backup vs priming branch ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-
- #Hereon we're doing backup work:
-
- #vvvvvvvvvvvvvvvvvvv Preliminary accounting and processing vvvvvvvvvvvvvvvvvvvv
- # mode - either 'full', 'incr', or 'verify'.
- # 'incr': default
- # 'full': if fullTrigger file exists
- # 'verify': with "verify" argument, do just preliminary stuff
- set mode=$1
- if ("$mode" != "verify") then
- if (-e $fullTrigger) then
- set mode='full'
- else
- set mode='incr'
- endif
- endif
-
- if ("$mode" != "full" && "$mode" != "incr" && "$mode" != "verify") then
- if ($interactive == 1) then
- echo Invalid mode: $mode
- getMode:
- echo -n "full, incr, verify, or quit? "
- set mode="$<"
- if ("$mode" != "full" && "$mode" != "incr" && "$mode" != "verify" \
- && "$mode" != "quit") goto getMode
- if ($mode == "quit") then
- guardedExit 0 # ====>
- endif
- else
- tolog echo ${scNm} ERROR: "Usage: ${scNm} [ verify ]"
- guardedExit 1 # ====>
- endif
- endif
-
- # Verify absence of and claim interlock:
- if (-e $interlock) then
- tolog echo ${scNm} INTERLOCK CONFLICT: \"$interlock\" interlock exists -
- tolog echo another backup may already be in progress.
- if ($interactive == 1) then
- echo -n "Clear interlock and continue? [y or (default:) n] "
- if ("$<" == "y") then
- rm $interlock
- goto interlockClear # vvvvv
- else
- endif
- endif
- tolog echo ${scNm} interlock collision - exiting
- guardedExit 1 # ====>
- endif
-
- interlockClear:
-
- set dateStamp=`date`
- echo $dateStamp > $interlock
- guard cleanupInterlock
-
- # Begin log entry for this backup session:
- tolog echo -n "${scNm} v ${version}: $mode mode "
- tolog echo $dateStamp
- tolog echo "Configuration Stage..."
- tolog echo -n " Host: `hostname`"
- tolog echo -n " Backup device: $realDev"
- if ($?debug == 1) then
- tolog echo -n " (in debug, dump to $toDev)"
- endif
- tolog echo " - designated capacity $totalCap"
- tolog echo " Subject paths: $subjPaths"
- tolog echo " Exclude paths: $excludePaths"
- tolog echo " Exclude dir basenames: $excludeDirs"
-
- # Add accounting dir to head of subjPaths so accounting resides at head of
- # archive, is fast and easy to retrieve:
-
- set subjPaths=($acctDir $subjPaths)
-
- # Normalize directories:
-
- # Filter paths to:
- # - reveal erroneous references
- # - normalize valid references to be "relative rooted" paths (paths prefixed
- # with './') whose contents are to be excluded from preservation; using
- # "relative rooted" paths with cpio will allow restoration of files, with
- # all the information about their path locations, to arbitary places (ie,
- # into the directory where resurrection is invoked).
- foreach curPathsVar (subjPaths excludePaths)
- set accumPath=""
- set pathExists=1
- # use getval as a command to get value of current paths variable:
- alias getval echo '$'$curPathsVar
- foreach aPath (`getval`)
- if (! -e $aPath) then
- tolog echo ${scNm} CONFIG ERROR: no such file \"$aPath\" "($curPathsVar)"
- set pathExists=0
- else
- set unraveled=`(cd $aPath; pwd)`
- if ($status == 1) then # bad dir:
- tolog echo ${scNm} CONFIG ERROR: \"$aPath\" not a directory "($curPathsVar)"
- set pathExists=0
- endif
- endif
- if ($pathExists == 0) then
- switch ($curPathsVar)
- case subjPaths:
- tolog echo " Nonexistant subject directory - drastic error, exiting."
- guardedExit 1 # ====>
- breaksw
- case excludePaths:
- tolog echo " Nonexistant exclude-directory - minor error, continuing."
- set unraveled=""
- endsw
- set pathExists=1 # reinit pathExists
- else
- set accumPath=($accumPath .$unraveled)
- endif
- end
- set $curPathsVar=($accumPath)
- end
-
- # Formulate fgrep clause:
- rm -f $fgrepClauseFile
- if ("$excludePaths" != "") then
- :>! $fgrepClauseFile
- foreach aPath ($excludePaths)
- echo $aPath >> $fgrepClauseFile
- end
- endif
-
- if ($mode == "verify") then
- tolog echo " Verification complete."
- guardedExit 0 # ====>
- endif
- #^^^^^^^^^^^^^^^^^^^ Preliminary accounting and processing ^^^^^^^^^^^^^^^^^^^^
-
-
-
- #vvvvvvvvvvvvvvvvvvvvvvvv Establish sequence info vvvvvvvvvvvvvvvvvvvvvvvvvvvvv
- # Establish new seqID and file, and get prior capacity.
- # Do echo to 'newSeqIDFile' last so mod time is as near find time as possible.
- tolog echo -n "Preliminary Accounting Stage... "
- tolog date +"%T %D"
- set seqID=0
- set capUsed=0
- if ($mode == "full") then
- if (-e $registry) mv -f $registry $priorRegistry
- if (-e $seqStatsFile) mv -f $seqStatsFile $priorStatsFile
- touch $registry
- touch $seqStatsFile
- else
- if (-e $priorStatsFile || -e $priorRegistry) then
- rm -f $priorStatsFile
- rm -f $priorRegistry
- endif
- # Get prior capacity
- if (! -z $seqStatsFile) then
- set capUsed=`tail -1 $seqStatsFile | awk '{print $2}'`
- endif
- if (-e $seqIDFile && ! -z $seqIDFile) then
- set seqID=`cat $seqIDFile`
- @ seqID += 1
- else
- set seqID=0
- endif
- endif
- guard cleanupSeqID
- tolog echo " Current seq id: $seqID"
- echo $seqID >! $newSeqIDFile
- #^^^^^^^^^^^^^^^^^^^^^^^ Established sequence info ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-
-
- #vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv Get to root vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
- cd /
- #^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Got to root ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-
- #vvvvvvvvvvvvvvvvvvvvvvvvvvvv Position tape vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
- tolog echo -n "Tape Positioning Stage... "
- tolog date +"%T %D"
- if ($?debug != 1) then
-
- set result=`mt -f $realDev rew`
- set exitVal=$status
- if ($exitVal != 0) then
- tolog echo ${scNm} Error: while rewinding tape $realDev -
- tolog echo " mt (rew): $result (exit value $exitVal)"
- tolog echo " Aborting..."
- guardedExit $exitVal
- else
- set result=`mt -f $realDev fsf $seqID`
- set exitVal=$status
- if ($exitVal != 0) then
- tolog echo ${scNm} Error: while positioning tape $realDev -
- tolog echo " mt (fsf $seqID): $result (exit value $exitVal)"
- tolog echo " Aborting..."
- guardedExit $exitVal
- endif
- endif
-
- else
- tolog echo " Tape positioning would be done now if debug weren't set."
- endif
- #^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Tape positioned ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-
-
-
- #vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv Compute rosters vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
- # NOTE Roster and extent of changed files is always computed; that of entire
- # subject hierarchy is computed only for full backup, and then that extent
- # is the one we're concerned with (though the roster of changed files is the
- # one that's registered, providing the seed for a registry of file-mod dates).
-
- tolog echo -n "Candidate-Computation Stage... "
- tolog date +"%T %D"
-
- # incrClause will make preserves respect the mod-time of seqIDFile unless
- # seqIDFile doesn't exist in which case no mod-time respective pruning is done:
- if (-e $seqIDFile) then
- set incrClause="-newer $seqIDFile "
- endif
-
- # Determine pruneClause from excludeDirs:
- foreach dir ( $excludeDirs )
- if ("$pruneClause" == "") then
- set pruneClause="-name $dir"
- else
- set pruneClause="$pruneClause -o -name $dir"
- endif
- end
- if ("$pruneClause" != "") then
- set pruneClause="( $pruneClause ) -prune"
- endif
-
- # awkFilter will take the find '-ls' format and print the filenames into
- # the filename argument, returning the total volume of the files.
- alias awkFilter awk -f $awkFilterScript argFile='\!*' -
-
- if ("$excludePaths" != "") then
- alias candidateFilter "excludePathsFilter | awkFilter"
- else
- alias candidateFilter "awkFilter"
- endif
- rm -f $pends
- set incrPruneClause=""
- if ("$pruneClause" != "") then
- set incrPruneClause="-a $pruneClause -o $pruneClause"
- endif
- set capPending=`find $subjPaths $incrClause -ls $incrPruneClause | candidateFilter $pends`
-
- if ($mode == "full") then
- rm -f $fullPends
- set capPending=`find $subjPaths -ls $pruneClause |candidateFilter $fullPends`
- endif
- #^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Computed rosters ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-
-
-
- #vvvvvvvvvvvvvvvvvvvv Verify sufficient capacity available vvvvvvvvvvvvvvvvvvvv
- tolog echo -n " Capacity pending this dump: $capPending bytes"
- set totalPending=$capUsed
-
- if (`echo "if ($totalCap < $capUsed + $capPending) 1" | bc` == "1") then
- tolog echo " "
- if ("$mode" == "full") then
- tolog echo ${scNm} CAPACITY OVERFLOW: Major problem
- tolog echo " full dump extent ($capPending) greater than designated $toDev"
- tolog echo " capacity ($totalCap). Distribute fs across multiple backups."
- guardedExit 1
- else
- tolog echo ${scNm} CAPACITY OVERFLOW: Remaining tape capacity insufficient
- tolog echo -n " for dump - total avail: $totalCap, prior used: $capUsed, "
- tolog echo "now pending: $capPending. Time to begin new tape."
- guardedExit 1 # ====V
- endif
- else
- set capUsed=`echo $capUsed + $capPending | bc`
- if ("$mode" == "incr") then
- tolog echo , brings total on tape to: $capUsed
- else
- tolog echo " "
- endif
- endif
- #^^^^^^^^^^^^^^^^^^^^ Verify sufficient capacity available ^^^^^^^^^^^^^^^^^^^^
-
-
-
- #vvvvvvvvvvvvvvvvvvvvvvvvvvv Archive files to tape vvvvvvvvvvvvvvvvvvvvvvvvvvvv
- tolog echo -n "File Archival Stage... "
- tolog date +"%T %D"
-
- guard cleanupTapeOverflow
-
- tolog echo -n " "
-
- doWrite:
- if ("$mode" == "incr") then
- tolog (cpio -oB < $pends | 8mmout $toDev)
- set exitVal=$status
- else
- tolog (cpio -oB < $fullPends | 8mmout $toDev)
- set exitVal=$status
- endif
-
- # NOTE that exitVal here reflects only the disposition of the '>'
- # redirection (which happens to be what we're concerned with)
- if ($exitVal != 0) then
- if (! $?retriedWrite) then
- # This nauseating finagle is necessary to rewrite intermediate files on
- # Exabyte drive (at the least for Perfect Byte st drivers under Sun OS4)
- set retriedWrite; set exitVal=0
- tolog echo "Hardening eof mark and retrying write..."
- tolog echo -n " "
- # Position to where an extra eof mark will solve the problem:
- mt -f $realDev rew; mt -f $realDev fsf $seqID; mt -f $realDev bsf 1
- # ... and write it:
- mt -f $realDev eof 1
- # ... then reposition to correct spot for writing:
- mt -f $realDev rew; mt -f $realDev fsf $seqID
- # ... and retry just once more:
- goto doWrite
- else
- tolog echo ${scNm}: "Error $exitVal writing on backup device $toDev - Aborting..."
- guardedExit $exitVal
- endif
- endif
- guard cleanupSeqID
- #^^^^^^^^^^^^^^^^^^^^^^^^^^^ Archived files to tape ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-
-
- #vvvvvvvvvvvvvvvvvvvvvvvv Verify integrity of dump vvvvvvvvvvvvvvvvvvvvvvvvvv
- # Cursorily read dump file into /dev/null to verify its integrity
- tolog echo -n "Dump integrity verification stage... "
- tolog date +"%T %D"
-
- set result=`mt -f $realDev rew`
- set exitVal=$status
- if ($exitVal != 0) then
- tolog echo ${scNm} Error: positioning tape $realDev during verification -
- tolog echo " mt (rew): $result (exit value $exitVal)"
- tolog echo " Aborting..."
- guardedExit $exitVal
- else
- set result=`mt -f $realDev fsf $seqID`
- set exitVal=$status
- if ($exitVal != 0) then
- tolog echo ${scNm} Error: positioning tape $realDev during verification -
- tolog echo " mt (fsf $seqID): $result (exit value $exitVal)"
- tolog echo " Aborting..."
- guardedExit $exitVal
- endif
- endif
-
- if ($?verifyTolerance) then
- if ($mode == "full") then
- set pendLines=`cat $fullPends | wc -l`
- else
- set pendLines=`cat $pends | wc -l`
- endif
- # Determine minimum and maximum toc lines within tolerance of pend lines:
- set tol=$pendLines; @ tol /= 100; @ tol *= $verifyTolerance
-
- set tapeTOC=`8mmin $realDev | cpio -it | wc -l`
-
- if ($tapeTOC < $pendLines - $tol) then
- tolog echo "${scNm} Error: archive table of contents too small -"
- tolog echo " Roster had $pendLines entries, archive had $tapeTOC."
- tolog echo " aborting..."
- guardedExit 1
- else if ($tapeTOC > $pendLines + $tol) then
- tolog echo "${scNm} Error: archive table of contents excessively large -"
- tolog echo " Roster had $pendLines entries, archive had $tapeTOC."
- tolog echo " aborting..."
- guardedExit 1
- endif
- else # do minimal verify:
- tolog (8mmin $realDev > /dev/null)
- set exitVal=$status
- if ($exitVal != 0) then
- tolog echo "${scNm} Error: $exitVal reading current dump - Aborting..."
- guardedExit $exitVal
- endif
- endif
- #^^^^^^^^^^^^^^^^^^^^^^^ Verified integrity of dump ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-
-
- #vvvvvvvvvvvvvvvvvvvvvvvvvv Commit new accounting vvvvvvvvvvvvvvvvvvvvvvvvvvvv
- tolog echo -n "Accounting Commitment Stage... "
- tolog date +"%T %D"
-
- if ($mode == "full") then
- if ($?debug != 1) then
-
- rm -f $registry
- # # Either convert fullPends to registry or start with empty registry:
- # if ($?exhaustiveRegistry == 1) then
- # strip leading './'s, snoc on <underscore>s, and sort fullPends:
- sed -e "s-./\(.*\)-\1 .-" $fullPends | grep -v "^ " | sort -o $registry
- # else
- # touch $registry
- # endif
-
- endif
- rm -f $fullTrigger
- else
- if (-e $fullPends) rm -f $fullPends
- endif
-
- # Put seqid in pends - strip leading '.'s, snoc on seqID's, and sort:
- sed -e "s-./\(.*\)-\1 $seqID-" $pends | grep -v "^ " | sort -o $pends
-
- # Merge $pends into $registry:
-
- # Use awk instead of join to do the merge of existing registry with new pends.
- # NOTE - 'join' would be perfectly suitable in stead of 'awk' here but
- # it has a serious and relevant bug - ref file 'join.bug' in distribution.
- # full and incr mode require different merge procedures:
- if ($mode == "full") then
- # full-mode-specific awk statement will reject repeated entries,
- # leaving either a "." terminated entry if the file was not present
- # in the incr pends, or else a "0" terminated entry if it was.
- alias modeAwk awk "'"'BEGIN { previous = ""; prevEntry = "" } \\
- $1 == prevEntry { previous = $0 } \\
- $1 != prevEntry { if (NR != 1) print previous \\
- previous = $0; prevEntry = $1 } \\
- END { if (NR != 1) print previous } ' "'" -
- else
- # incr-mode-specific awk statement will merge in additional entries,
- # creating new entries or adding new seqids to existing instances if any.
- alias modeAwk awk "'"'BEGIN { previous = ""; prevMark = ""} \\
- $1 == previous { if ($NF != prevMark) \\
- { printf " %s",$NF \\
- prevMark = $NF } } \\
- $1 != previous { if (NR != 1) printf "\n" \\
- printf "%s", $0 \\
- previous = $1; prevMark = $NF } \\
- END { if (NR != 1) printf "\n" } ' "'" -
- endif
-
- # Enter new seqIDs in registry:
- cat $registry $pends | sort -b +0 -1 +1 -2n - | modeAwk >! ${registry}.tmp
-
- # Commit new accounting to place:
- if ($?debug != 1) then
- mv ${registry}.tmp $registry
- rm -f $pends
- # Commit new statistics:
- echo -n "$seqID $capUsed $toDev $mode" >> $seqStatsFile
- if ($?verifyTolerance) then
- echo -n " ${tapeTOC}~${pendLines}%${verifyTolerance}" >> $seqStatsFile
- else
- echo -n " cursory" >> $seqStatsFile
- endif
- echo -n " ($subjPaths)" >> $seqStatsFile
- echo -n " ($excludePaths)" >> $seqStatsFile
- echo -n " ($excludeDirs)" >> $seqStatsFile
- echo " $dateStamp" >> $seqStatsFile
-
- # Establish new seqID in place of previous one:
- mv -f $seqIDFile{,.prev}
- mv $newSeqIDFile $seqIDFile
- else
- tolog echo In debug mode - not committing accounting.
- endif
-
-
- guard cleanupInterlock
- #^^^^^^^^^^^^^^^^^^^^^^^^^ Committed new accounting ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-
-
- #vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv Finish vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
- rm $interlock
- guard ground
- tolog echo Successful Completion.
- tolog echo -n " start: $dateStamp, finish: "
- tolog date
- guardedExit 0
- #^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ DONE ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-
-
- #vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv guarded exits vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
- cleanupTapeOverflow:
- guard cleanupSeqID
- #rm -f $pends
-
- cleanupSeqID:
- guard cleanupInterlock
- rm -f $newSeqIDFile
-
- cleanupInterlock:
- guard ground
- rm -f $interlock
-
- ground:
- onintr
-
- # Do notification of backup error if non-zero exitVal:
- if ($exitVal != 0) then
- tolog echo "${scNm}: `date` - error notification ..."
- set reply="y"
- if ("$interactive" == 1) then
- tolog echo "${scNm}: A non-zero exit has been encountered."
- readReply:
- tolog echo -n " Send mail notification (y or n)? "
- set replyRead="$<"
- switch ("$replyRead")
- case "n":
- case "N":
- set reply="n"
- breaksw
- case "y":
- case "Y":
- breaksw
- default:
- tolog echo "Please respond with either 'y' or 'n':"
- goto readReply
- endsw
- tolog echo $reply
- endif
- if ("$reply" != "n") then
- tolog echo " Sending mail notification..."
- set hostNm=`hostname`
- mail -s "Backup error on $hostNm" $errorNoticeTo << EONotice
- A $exitVal exit value has occurred in the course of an ${scNm} run on
- host $hostNm.
-
- Details of the run can be found at the end of the backup log file,
- $logfile
-
- The relevant entry is for time stamp $dateStamp.
- EONotice
- tolog echo "... Bye."
- else
- tolog echo "No mail notification sent; bye."
- endif
- endif
- if ("$scNm" != "fullPrimer") then
- tolog echo "=================================================================="
- endif
-
- exit $exitVal # ====>
- #^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ guarded exits ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-